在前面的文章中,我們知道知道每一個類別裡面都包含其屬性及方法,我們必須透過實例化的動作,才能夠取用。
例如:
class Log {
String message;
Log(this.message);
void sendMessage(){
//send message
print('message sent');
}
}
void main(){
final log = Log('System boot');
print(log.message);
log.sendMessage();
}
這裡面的屬性、函數,都是在我們建立類別之後才去初始化的。所以我們才能從類別的實例去取用它們。
那麼,有沒有什麼方法能夠不將類別實例化,直接取用該類別的屬性或是函數呢?
有,靜態變數(static variable)、靜態方法(static method)。
Dart 提供了 static 關鍵字,讓類別的屬性、函數轉變為全域的變數以及方法。
將類別的屬性加上 static
,該屬性成為全域變數。
範例:替 Log 類加上一個靜態變數 tag。
class Log {
static String tag;
String message;
Log(this.message);
void sendMessage(){
//send message
print('message sent');
}
}
我們可以直接使用它,而不經過實例化 Log 類。
void main(){
final tag = Log.tag;
print(tag);
}
// null
由上面的範例得知,當靜態變數未設定初始值,則值為 null。
修改一下上面的範例,讓該靜態變數是由外部取得。我們來看一下靜態變數的初始化。
class Log {
static String tag = TAG('Dart').name;
String message;
Log(this.message);
void sendMessage(){
//send message
print('message sent');
}
}
class TAG{
String name;
TAG(this.name){
print('Tag is $name');
}
}
void main(){
final tag1 = Log.tag;
print(tag1);
final tag2 = Log.tag;
print(tag2);
Log.tag = 'Dart2';
print(Log.tag);
}
//Tag is Dart
//Dart
//Dart
//Dart2
靜態變數只有在第一次使用的時候進行初始化,之後每次呼叫,都只是從記憶體取出該值而已。
覆寫靜態變數之後,每次取值都是新的值。
我們亦可以在靜態變數上加上 const
來宣告該靜態變數的值為常數。
class Log{
static const bool isDebug = true;
static String tag = TAG('Dart').name;
String message;
Log(this.message);
void sendMessage(){
//send message
if(isDebug){
print('message sent');
}
}
}
如同靜態變數,將類別中的方法加上 static
關鍵字即成為靜態方法,靜態方法是可以不需要實例化類別而進行操作的方法。
例如,我們可以將上例的 sendMessage()
改為靜態方法
class Log {
static const bool isDebug = true;
static String tag = TAG('Dart').name;
String message;
Log(this.message);
static void sendMessage(Log log){
//send message
print('[$tag]:${log.message}');
}
}
void main(){
Log.sendMessage(Log('Message test'));
}
//[Dart]:Message test
static
變成靜態方法之後,沒有辦法直接取得類別屬性 String message
。sendMessage
的引數改為 Log log
,將整個類別傳進來,就可以直接在靜態方法 seneMessage
中取用傳入的 log 類別。static String tag
有沒有發現,其實靜態方法不一定需要存在於類別內,因為我們可以將整個類別當作引數傳入,那麼這整個靜態方法就不一定需要擺在類別裡面了。
我們可以將靜態方法搬出類別嗎?
不行,但是 Dart 提供了頂層函數 (top-level functions) 可以達成一樣的目的。
將上方的範例中的靜態方法改為頂層函數:
void sendMessage(Log log){
//send message
print('[${Log.tag}]:${log.message}');
}
class Log {
static const bool isDebug = true;
static String tag = TAG('Dart').name;
String message;
Log(this.message);
}
static
關鍵字移除。如此,就可以將靜態方法改為頂層函數。
呼叫方法:只要直接使用函數的名稱,即可呼叫該函數。
void main(){
sendMessage(Log('Message test'));
}
//[Dart]:Message test
main()
也是屬於頂層函數,因為他不屬於任何類別。有頂層函數,當然就有頂層變數以及頂層常數。
定義頂層變數、頂層常數的方式也很簡單。將原本定義在類別內的靜態變數、靜態常數移出類別,並將 static
關鍵字移除。
const bool isDebug = true;
String tag= TAG('Dart').name;
void sendMessage(Log log){
//send message
print('[$tag]:${log.message}');
}
class Log {
String message;
Log(this.message);
}
如此,類別內就只剩下非靜態的屬性、方法。變得單純許多。
靜態變數、靜態方法在程式語言中是一定會存在的項目,使用靜態項目常常被質疑違反了物件導向的設計。因為不需要將類別實例化就可以使用該變數或方法。本篇文章不討論使用的好處與壞處,純粹只是介紹靜態項目的用法。
在 Effective Dart 提到,避免建立一個類別只存在一個靜態項目,若是有這樣使用靜態項目的需求,可以改用頂層函數、頂層變數、頂層常數來替代。